package sql2struct

import (
	"fmt"
	"go-programming-tour-book/tour/internal/word"
	"html/template"
	"os"
)

// 模版对象声明
const structTpl = `type {{.TableName | ToCamelCase}} struct {
{{range .Columns}}{{ $length := len .Comment}}{{ if gt $length 0 }}
//{{.Comment}}{{else}}// {{.Name}} {{ end }}{{ $typeLen := len .Type }} 
{{ if gt $typeLen 0 }}{{.Name | ToCamelCase}} {{.Type}}	{{.Tag}}{{ else }}{{.Name}}{{ end }}{{end}}
}

func (model {{.TableName | ToCamelCase}}) TableName() string {
	return "{{.TableName}}"
}`

/*
上述预定义模版生成后的原型大致如下：
type 大写驼峰的表名称 struct {
	//注释
	字段名 字段类型
	//注释
	字段名 字段类型
	...
}

func (model 大写驼峰的表名称) TableName() string {
	return "表名称"
}
*/

type StructTemplate struct {
	structTpl string
}

// StructColumn 存储转换后的 Go 结构体中的所有字段信息
type StructColumn struct {
	Name    string
	Type    string
	Tag     string
	Comment string
}

// StructTemplateDB 存储最终用于渲染的模版对象信息
type StructTemplateDB struct {
	TableName string
	Columns   []*StructColumn
}

// NewStructTemplate 创建新的模版对象
func NewStructTemplate() *StructTemplate {
	return &StructTemplate{structTpl: structTpl}
}

// AssemblyColumns 通过查询 COLUMNS 表所组装得到的 tbColumns 进行进一步的分解和转换。
// eg：数据库类型到 Go结构体的转换；对 JSON Tag 的处理
func (t *StructTemplate) AssemblyColumns(tbColumns []*TableColumn) []*StructColumn {
	tplColumns := make([]*StructColumn, 0, len(tbColumns))
	for _, column := range tbColumns {
		//对 JSON Tag 的处理
		tag := fmt.Sprintf("`json:\"%s\"`", column.ColumnName)
		//数据库类型到 Go结构体的转换
		tplColumns = append(tplColumns, &StructColumn{
			Name:    column.ColumnName,
			Type:    DBTypeToStructType[column.DataType],
			Tag:     tag,
			Comment: column.ColumnComment,
		})
	}
	return tplColumns
}

// Generate 对模版渲染的自定义函数和模版对象进行处理
func (t *StructTemplate) Generate(tableName string, tplColumns []*StructColumn) error {
	//组装出符合预定义模版的模版对象
	//声明了一个名为 sql2struct 的新模版对象
	tpl := template.Must(template.New("sql2struct").Funcs(template.FuncMap{
		"ToCamelCase": word.UnderscoreToUpperCamelCase, //定义了自定义函数 ToCamelCase，并与 word.UnderscoreToUpperCamelCase 方法进行了绑定
	}).Parse(t.structTpl))

	tplDB := StructTemplateDB{
		TableName: tableName,
		Columns:   tplColumns,
	}

	err := tpl.Execute(os.Stdout, tplDB) //模版渲染
	if err != nil {
		return err
	}
	return nil
}
